/** This class provides extra functionality that Canvas3D does not have.  Also, the functions
 * that overides Canvas3D methods are not cached so that there is not a frame lag when used
 * in WakeUpOnElapsedFrame as long as the View Model and TransformGroups above the ViewPlatform
 * are changed before these functions are used.
 *
 * This class assumes:
 *  The ViewPlatform has capabilities set to ALLOW_LOCAL_TO_VWORLD_READ
 * <p>
 * @author Blaine Bell
 * @since JDK1.1
 * @version 1.0
 *
 **/

import javax.media.j3d.*;
import javax.vecmath.*;
import java.awt.*;
import java.awt.event.*;


public class FixedCanvas3D extends Canvas3D implements ComponentListener{

	private static final long serialVersionUID = 1L;
	double _fclipdist=0.,_bclipdist=0.;

	public void refreshAllVariables(){
		if (getView()!=null){
			computeFrontClipValueInImagePlate();
			computeBackClipValueInImagePlate();
		}
	}
	
	public void componentHidden(ComponentEvent e){
		refreshAllVariables();
	}
	
	public void componentShown(ComponentEvent e){
		refreshAllVariables();
	}
	
	public void componentMoved(ComponentEvent e){
		refreshAllVariables();
	}
	
	public void componentResized(ComponentEvent e){
		refreshAllVariables();
	}

	/* Constructors */
	public FixedCanvas3D(java.awt.GraphicsConfiguration graphicsConfiguration){
		super(graphicsConfiguration);
		addComponentListener(this);
		refreshAllVariables();
	}
	public FixedCanvas3D(java.awt.GraphicsConfiguration graphicsConfiguration,
			boolean offScreen){
		super(graphicsConfiguration,offScreen);
		addComponentListener(this);
		refreshAllVariables();
	}

	public double getFrontClipValueInImagePlate(){
		return (_fclipdist);
	}
	public double computeFrontClipValueInImagePlate(){
		int fviewpolicy=getView().getFrontClipPolicy();
		_fclipdist = -getView().getFrontClipDistance();

		if (fviewpolicy==View.VIRTUAL_EYE || fviewpolicy==View.VIRTUAL_SCREEN){
			/** need to multiply it by virtual-phyiscal scale **/ 
			if (getView().getWindowResizePolicy()==View.PHYSICAL_WORLD){
				/* Resize policy has effect on clipping planes */
				_fclipdist *= getPhysicalWidth()/2.;
			} else {
				_fclipdist *= getScreen3D().getPhysicalScreenWidth()/2.;
			}
		} else if (getView().getWindowResizePolicy()==View.PHYSICAL_WORLD){
			_fclipdist *= getPhysicalWidth()/getScreen3D().getPhysicalScreenWidth();
		}
		if (fviewpolicy==View.PHYSICAL_EYE || fviewpolicy==View.VIRTUAL_EYE){
			Point3d eyepoint = new Point3d();
			getCenterEyeInImagePlate(eyepoint);
			_fclipdist += eyepoint.z;
		}
		return (_fclipdist);
	}

	public double getBackClipValueInImagePlate(){
		return (_bclipdist);
	}
	public double computeBackClipValueInImagePlate(){
		int bviewpolicy=getView().getBackClipPolicy();
		_bclipdist = -getView().getBackClipDistance();

		if (bviewpolicy==View.VIRTUAL_EYE || bviewpolicy==View.VIRTUAL_SCREEN){
			/** need to multiply it by virtual-phyiscal scale **/ 
			if (getView().getWindowResizePolicy()==View.PHYSICAL_WORLD){
				/* Resize policy has effect on clipping planes */
				_bclipdist *= getPhysicalWidth()/2.;
			} else {
				_bclipdist *= getScreen3D().getPhysicalScreenWidth()/2.;
			}
		} else if (getView().getWindowResizePolicy()==View.PHYSICAL_WORLD){
			_bclipdist *= getPhysicalWidth()/getScreen3D().getPhysicalScreenWidth();
		}
		if (bviewpolicy==View.PHYSICAL_EYE || bviewpolicy==View.VIRTUAL_EYE){
			Point3d eyepoint = new Point3d();
			getCenterEyeInImagePlate(eyepoint);
			_bclipdist += eyepoint.z;
		}
		return (_bclipdist);
	}

	public void getImagePlateToVworld(Transform3D t){
		ViewPlatform viewplatform=getView().getViewPlatform();
		Transform3D vp2vw = new Transform3D();
		viewplatform.getLocalToVworld(vp2vw);
		getViewPlatformToImagePlate (t);
		t.mul(vp2vw,t);
	}
	public void getViewPlatformToImagePlate (Transform3D t){
		View view=getView();
		ViewPlatform viewplatform=getView().getViewPlatform();
		int resizepolicy = view.getWindowResizePolicy(),
		movementpolicy = view.getWindowMovementPolicy(),
		viewattachmentpolicy = viewplatform.getViewAttachPolicy();
		Screen3D screen = getScreen3D();
		Vector2d canvasPhysDimensions = null,
		screenPhysDimensions = new Vector2d(screen.getPhysicalScreenWidth(),
				screen.getPhysicalScreenHeight());
		Point canvasLocation = null;
		Dimension screenPixSize = null,canvasPixSize = null;
		/** Need to compute canvas coordinates in imageplate using location on screen */
		screenPixSize = screen.getSize();
		try {
			canvasLocation = getLocationOnScreen();
		} catch (Exception e){
			t.setIdentity();
			return;
		}
		canvasPixSize = getSize();
		/** getLocationOnScreen returns from upper left, we want lower left */
		canvasLocation.y = screenPixSize.height-canvasLocation.y-canvasPixSize.height;

		canvasPhysDimensions = new 
		Vector2d(screenPhysDimensions.x*canvasPixSize.width/screenPixSize.width,

				screenPhysDimensions.y*canvasPixSize.height/screenPixSize.height);
		t.setIdentity();
		Vector3d translation=new Vector3d();
		double scale=1;
		if (resizepolicy==View.VIRTUAL_WORLD){
			/** View Platform in center of screen */
			scale = .5*screenPhysDimensions.x;
		} else {
			/* The ratio of the widths between the canvas and screen */
			scale = .5*canvasPhysDimensions.x;
		}
		t.setScale(scale);
		if (movementpolicy==View.VIRTUAL_WORLD){
			/** Middle of screen */
			translation.x = .5*screenPhysDimensions.x;
			translation.y = .5*screenPhysDimensions.y;
		} else {
			/** Size of Screen in VWorld is 2 wide 
			 * since scale is */
			translation.x = screenPhysDimensions.x*(canvasLocation.x + 
					canvasPixSize.width/2.)/screenPixSize.width;
			translation.y = screenPhysDimensions.y*(canvasLocation.y + 
					canvasPixSize.height/2.)/(double)screenPixSize.height;
		}

		if (view.getWindowEyepointPolicy()==View.RELATIVE_TO_FIELD_OF_VIEW){
			if (viewattachmentpolicy==View.NOMINAL_SCREEN){
				translation.z = 0.;
			} else if (viewattachmentpolicy==View.NOMINAL_HEAD){
				translation.z = 
					.5*canvasPhysDimensions.x/Math.tan(view.getFieldOfView()/2.);
			} else if (viewattachmentpolicy==View.NOMINAL_FEET){
				translation.y -= 
					view.getPhysicalBody().getNominalEyeHeightFromGround()*scale/(.5*screenPhysDimensions.x);;

					translation.z = 
						.5*canvasPhysDimensions.x/Math.tan(view.getFieldOfView()/2.);
			}
		} else {
			if (viewattachmentpolicy==View.NOMINAL_SCREEN){
				translation.z = 0.;
			} else if (viewattachmentpolicy==View.NOMINAL_HEAD){
				translation.z = 
					view.getPhysicalBody().getNominalEyeOffsetFromNominalScreen();
			} else if (viewattachmentpolicy==View.NOMINAL_FEET){
				translation.y -= view.getPhysicalBody().getNominalEyeHeightFromGround();
				translation.z = 
					view.getPhysicalBody().getNominalEyeOffsetFromNominalScreen();
			}
		}
		t.setTranslation(translation); 
		try {
			t.invert();
		} catch (Exception e){
			System.out.println("t=" + t);
			e.printStackTrace();
			System.out.println("canvasLocation=" + canvasLocation + " canvasPixSize=" + canvasPixSize + " screenPhysDimensions=" + screenPhysDimensions + " canvasPhysDimensions=" + canvasPhysDimensions);
		}
	}
	public void getImagePlateToViewPlatform(Transform3D t){
		getViewPlatformToImagePlate (t);
		t.invert();
	}

	public void getVworldToImagePlate(Transform3D t){
		getImagePlateToVworld(t);
		t.invert();
	}
	public void getPixelLocationInImagePlate(int x, int y, Point3d imagePlatePoint){
		double dx=x, dy=y;
		Screen3D screen = getScreen3D();
		Dimension screenPixSize = screen.getSize();
		Point canvasLocation = getLocationOnScreen();
		Vector2d screenPhysDimensions = new Vector2d(screen.getPhysicalScreenWidth(), screen.getPhysicalScreenHeight());
		imagePlatePoint.x = screenPhysDimensions.x*((canvasLocation.x + 
				dx)/(double)screenPixSize.width);
		imagePlatePoint.y = screenPhysDimensions.y*((screenPixSize.height-canvasLocation.y 
				- dy-1)/(double)screenPixSize.height);
		imagePlatePoint.z = 0.;
	}
	public void getPixelLocationInImagePlate(Point2d pixelLocation, Point3d 
			imagePlatePoint){

		getPixelLocationInImagePlate((int)Math.round(pixelLocation.x),(int)Math.round(pixelLocation.y),
				imagePlatePoint);
	}
}

